/*
 *  This file is part of MRP (http://mrp.codehaus.org/).
 *
 *  This file is licensed to You under the Eclipse Public License (EPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/eclipse-1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.binarytranslator.arch.ppc.os.process.linux;

import org.binarytranslator.DBT_Options;
import org.binarytranslator.arch.ppc.os.process.PPC_ProcessSpace;
import org.binarytranslator.arch.ppc.os.abi.linux.PPC_LinuxSystemCalls;
import org.binarytranslator.generic.execution.GdbController.GdbTarget;
import org.binarytranslator.generic.os.abi.linux.LinuxStackInitializer;
import org.binarytranslator.generic.os.abi.linux.LinuxSystemCallGenerator;
import org.binarytranslator.generic.os.abi.linux.LinuxSystemCalls;
import org.binarytranslator.generic.os.loader.Loader;
import org.binarytranslator.generic.os.loader.elf.ELF_Loader;
import org.binarytranslator.generic.os.process.ProcessSpace;

/**
 * Simulate the process address space for our PowerPC ELF binary. Also keep
 * track of register contents (currently only those used in user models UISA and
 * VEA).
 * 
 * @author Richard Matley, Ian Rogers
 * 
 */
final public class PPC_LinuxProcessSpace extends PPC_ProcessSpace implements
    LinuxSystemCallGenerator {
  /**
   * System calls object for handling system calls generated by this process
   */
  final LinuxSystemCalls syscalls;

  /**
   * Provides access to the single arguments of a system call.
   */
  final PPC_LinuxSyscallArgumentIterator syscallArgs;

  /**
   * The top of the stack
   */
  private static final int STACK_TOP = 0x80000000;

  /**
   * Auxiliary vector
   */
  private int[] auxVector;
  
  /**
   * Constructor
   */
  public PPC_LinuxProcessSpace(Loader loader) {
    syscallArgs = new PPC_LinuxSyscallArgumentIterator(this);
    syscalls = new PPC_LinuxSystemCalls(this);
  }

  /**
   * Initialise the process space, called after the binary has been loaded
   * 
   */
  @Override
  public void initialise(Loader loader) {
    this.pc = loader.getEntryPoint();
    this.r1 = initialiseStack(loader, pc);
    syscalls.initialize(loader.getBrk());
  }

  /**
   * Initialise the stack
   */
  private int initialiseStack(Loader loader, int pc) {
    auxVector = new int[]{
        LinuxStackInitializer.AuxiliaryVectorType.AT_IGNOREPPC,
        LinuxStackInitializer.AuxiliaryVectorType.AT_IGNOREPPC,
        LinuxStackInitializer.AuxiliaryVectorType.AT_IGNOREPPC,
        LinuxStackInitializer.AuxiliaryVectorType.AT_IGNOREPPC,

        LinuxStackInitializer.AuxiliaryVectorType.AT_DCACHEBSIZE,
        0x00,
        LinuxStackInitializer.AuxiliaryVectorType.AT_ICACHEBSIZE,
        0x00,
        LinuxStackInitializer.AuxiliaryVectorType.AT_UCACHEBSIZE,
        0x0,

        LinuxStackInitializer.AuxiliaryVectorType.AT_HWCAP,
        0x9c000000,

        LinuxStackInitializer.AuxiliaryVectorType.AT_PAGESZ,
        0x1000,

        LinuxStackInitializer.AuxiliaryVectorType.AT_CLKTCK,
        0x64,

        LinuxStackInitializer.AuxiliaryVectorType.AT_PHDR,
        ((ELF_Loader)loader).getProgramHeaderAddress(),
        LinuxStackInitializer.AuxiliaryVectorType.AT_PHNUM,
        ((ELF_Loader)loader).getNumberOfProgramSegmentHeaders(),
        LinuxStackInitializer.AuxiliaryVectorType.AT_PHENT,
        ((ELF_Loader)loader).getProgramSegmentHeaderSize(),

        //LinuxStackInitializer.AuxiliaryVectorType.AT_BASE, 0x0,
        LinuxStackInitializer.AuxiliaryVectorType.AT_FLAGS, 0x0,
        LinuxStackInitializer.AuxiliaryVectorType.AT_ENTRY, pc,
        LinuxStackInitializer.AuxiliaryVectorType.AT_UID, DBT_Options.UID,
        LinuxStackInitializer.AuxiliaryVectorType.AT_EUID, DBT_Options.UID,
        LinuxStackInitializer.AuxiliaryVectorType.AT_GID, DBT_Options.GID,
        LinuxStackInitializer.AuxiliaryVectorType.AT_EGID, DBT_Options.GID,
        LinuxStackInitializer.AuxiliaryVectorType.AT_SECURE, 0x0,
        LinuxStackInitializer.AuxiliaryVectorType.AT_NULL, 0x0 };
    /*
     * The cache sizes and flags are as for a test program running on the iBook,
     * softwood. AT_BASE will need to be changed for dynamically linked
     * binaries.
     */

    return LinuxStackInitializer.stackInit(memory, STACK_TOP, 
        getEnvironmentVariables(), auxVector);
  }

  /**
   * Entry point for system calls
   */
  public void doSysCall() {
    syscalls.doSysCall();
  }

  // -oO LinuxSystemCallGenerator interface Oo-

  /**
   * Return the system call number from the generator
   */
  public int getSysCallNumber() {
    return r0;
  }

  /**
   * Create an array of arguments for the system call
   * 
   * @return array of system call argument values
   */
  public CallArgumentIterator getSysCallArguments() {

    syscallArgs.reset();
    return syscallArgs;
  }

  /**
   * Set the return value for a system call
   * 
   * @param r
   *          the return value
   */
  public void setSysCallReturn(int r) {
    r3 = r;
  }

  /**
   * Set an error value for a system call
   * 
   * @param r
   *          the error value
   */
  public void setSysCallError(int r) {
    r3 = r;
    setCR_bit(0, true);
  }

  public GdbTarget getGdbTarget() {
    return this;
  }

  public int[] getAuxVector() {
    return auxVector;
  }

  public int getGDBFrameBaseRegister() {
    return -1;
  }

  public boolean hasFrameBaseRegister() {
    return false;
  }

  public ProcessSpace getProcessSpace() {
    return this;
  }

}
